package com.sdp.right.utils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class RsaUtil2 {

	/**
	 * AES_ALGORITHM常量定义了对称加密算法AES的名称
	 */
	private static final String AES_ALGORITHM = "AES";
	/**
	 * AES_KEY_SIZE常量定义了AES密钥的长度
	 */
	private static final int AES_KEY_SIZE = 256;
	/**
	 * RSA_ALGORITHM常量定义了非对称加密算法RSA的名称
	 */
	private static final String RSA_ALGORITHM = "RSA";

	private static Map<String, String> keyMap = new HashMap<>();
	
	public static String getpublicKey() {
		return keyMap.get("publicKeyStr");
	}
    
    public static String getprivateKey() {
		return keyMap.get("privateKeyStr");
	}

	/**
	 * 生成密钥对
	 *
	 * @return 返回包含公私钥的map
	 */
	public static Map<String, String> generateKey() {
		KeyPairGenerator keygen;
		try {
			keygen = KeyPairGenerator.getInstance(RSA_ALGORITHM);
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException("RSA初始化密钥出现错误,算法异常");
		}
		SecureRandom secrand = new SecureRandom();
		// 初始化随机产生器
		secrand.setSeed("Alian".getBytes());
		// 初始化密钥生成器
		keygen.initialize(2048, secrand);
		KeyPair keyPair = keygen.genKeyPair();
		// 获取公钥并转成base64编码
		byte[] pub_key = keyPair.getPublic().getEncoded();
		String publicKeyStr = Base64.getEncoder().encodeToString(pub_key);
		// 获取私钥并转成base64编码
		byte[] pri_key = keyPair.getPrivate().getEncoded();
		String privateKeyStr = Base64.getEncoder().encodeToString(pri_key);
		// 创建一个Map返回结果
		Map<String, String> keyPairMap = new HashMap<>();
		keyPairMap.put("publicKeyStr", publicKeyStr);
		keyPairMap.put("privateKeyStr", privateKeyStr);
		keyMap = keyPairMap;
		return keyPairMap;
	}

	/**
	 * 使用RSA公钥加密消息
	 *
	 * @param plainText
	 *            明文
	 * @param publicKey
	 *            公钥
	 * @return 加密后的字符串
	 */
	public static String encrypt(String plainText, String publicKeyString) throws Exception {
		
		PublicKey publicKey = getPublicKeyFromString(publicKeyString);
		// 生成随机AES密钥以进行对称加密
		KeyGenerator keyGen = KeyGenerator.getInstance(AES_ALGORITHM);
		keyGen.init(AES_KEY_SIZE);
		SecretKey secretKey = keyGen.generateKey();

		// 使用AES加密明文
		Cipher aesCipher = Cipher.getInstance(AES_ALGORITHM);
		aesCipher.init(Cipher.ENCRYPT_MODE, secretKey);
		byte[] encryptedBytes = aesCipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));

		// 使用RSA加密AES密钥
		Cipher rsaCipher = Cipher.getInstance(RSA_ALGORITHM);
		rsaCipher.init(Cipher.ENCRYPT_MODE, publicKey);
		byte[] encryptedAESKey = rsaCipher.doFinal(secretKey.getEncoded());

		// 组合AES密钥和加密的消息
		String encryptedAESKeyStr = Base64.getEncoder().encodeToString(encryptedAESKey);
		String encryptedMessageStr = Base64.getEncoder().encodeToString(encryptedBytes);

		return encryptedAESKeyStr + ":" + encryptedMessageStr;
	}

	/**
	 * 使用RSA私钥解密消息
	 *
	 * @param encryptedText
	 *            密文
	 * @param privateKey
	 *            私钥
	 * @return 解密后的字符串
	 */
	public static String decrypt(String encryptedText, String privateKeyStr) throws Exception {
		PrivateKey privateKey = getPrivateKeyFromString(privateKeyStr);
		
		// 将输入分成加密的AES密钥和加密的消息两部分
		String[] parts = encryptedText.split(":");
		if (parts.length != 2) {
			throw new IllegalArgumentException("无效的输入格式");
		}

		String encryptedAESKeyStr = parts[0];
		String encryptedMessageStr = parts[1];

		// 使用RSA私钥解密AES密钥
		Cipher rsaCipher = Cipher.getInstance(RSA_ALGORITHM);
		rsaCipher.init(Cipher.DECRYPT_MODE, privateKey);
		byte[] encryptedAESKey = Base64.getDecoder().decode(encryptedAESKeyStr);
		byte[] decryptedAESKey = rsaCipher.doFinal(encryptedAESKey);

		// 使用AES解密消息
		SecretKey secretKey = new SecretKeySpec(decryptedAESKey, 0, decryptedAESKey.length, AES_ALGORITHM);
		Cipher aesCipher = Cipher.getInstance(AES_ALGORITHM);
		aesCipher.init(Cipher.DECRYPT_MODE, secretKey);
		byte[] decryptedBytes = aesCipher.doFinal(Base64.getDecoder().decode(encryptedMessageStr));

		return new String(decryptedBytes, StandardCharsets.UTF_8);
	}

	// 从字符串格式的公钥创建 PublicKey 对象
	private static PublicKey getPublicKeyFromString(String publicKeyString) throws Exception {
		byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyString);
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
		return keyFactory.generatePublic(keySpec);
	}

	// 从字符串格式的私钥创建 PrivateKey 对象
	private static PrivateKey getPrivateKeyFromString(String privateKeyString) throws Exception {
		byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyString);
		PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
		return keyFactory.generatePrivate(keySpec);
	}

	public static void main(String[] args) throws Exception {

		Map<String, String> keyMap = RsaUtil2.generateKey();
		String publicKeyStr = keyMap.get("publicKeyStr");
		String privateKeyStr = keyMap.get("privateKeyStr");
		System.out.println("-----------------生成的公钥和私钥------------------------------");
		System.out.println("获取到的公钥：" + publicKeyStr);
		System.out.println("获取到的私钥：" + privateKeyStr);

		
		// 从字符串格式的公钥和私钥创建公钥和私钥对象
		

		// 加密的密文
		String plainText = "报错问题解释在Java中使用RSA解密时，如果遇到“字符过长”的错误，这通常意味着你试图解密的数据超过了RSA算法支持的最大块大小。RSA算法本身限制了它可以加密的数据块的大小，这个大小由密钥长度决定。对于一个2048位的密钥，可以加密的数据最大为256字节（大约2048位/8字节）。解决方法：使用较小的密钥，但这会降低安全性。对数据进行分块处理：将数据分成小块，每个块的大小不超过密钥允许的最大值。对每个块进行单独加密。接收方再对它们进行解密并重新组合。使用对称加密算法（如AES）加密数据，并仅使用RSA加密对称密钥。使用数据格式，如JSON，它有内置的支持来处理大量数据的分割和重组。";
	
		// 使用公钥进行加密
		String encryptedText = encrypt(plainText, publicKeyStr);

		System.out.println("Encrypted: " + encryptedText);

		// 使用私钥进行解密
		String decryptedText = decrypt(encryptedText, privateKeyStr);
		// 打印解密结果
		System.out.println("Decrypted: " + decryptedText);
	}

}
